package cn.dansj.common.utils.lock;

import cn.dansj.common.utils.bean.DBConnection;
import cn.dansj.common.utils.reflect.ClassUtils;
import cn.dansj.common.utils.transfer.Transformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

public class LockFactory {
    private static final Logger logger = LoggerFactory.getLogger(LockFactory.class);
    // 锁类型注册表
    private static final Map<String, LockCreator> lockCreators = new ConcurrentHashMap<>();
    // 是否已扫描自动注册类
    private static final AtomicBoolean autoRegistered = new AtomicBoolean(false);
    // 扫描的基础包名
    private static String autoScanBasePackage = "cn.dansj";

    /**
     * 设置自动扫描的基础包名
     *
     * @param basePackage 基础包名
     */
    public static void setAutoScanBasePackage(String basePackage) {
        autoScanBasePackage = basePackage;
        autoRegistered.set(false); // 重置扫描状态
    }

    public static List<String> getAllLockNames() {
        scanAndRegisterAutoLocks();
        return new ArrayList<>(lockCreators.keySet());
    }

    private static String formatLockName(String lockName) {
        return lockName.toLowerCase();
    }

    /**
     * 扫描并注册所有自动注册锁
     */
    private static void scanAndRegisterAutoLocks() {
        if (autoRegistered.get()) return;

        if (autoRegistered.compareAndSet(false, true)) {
            ClassUtils.getSubTypeClasses(AbstractLock.class, AutoRegisterLock.class, autoScanBasePackage).stream().filter(clazz -> !ClassUtils.isAbstract(clazz)).forEach(clazz -> {
                AutoRegisterLock annotation = clazz.getAnnotation(AutoRegisterLock.class);
                String lockType = Transformation.nvl(annotation.value(), clazz.getSimpleName());
                // 注册锁类
                try {
                    registerLockType(formatLockName(lockType), clazz, annotation.noConnection());
                } catch (IllegalArgumentException illegalArgumentException) {
                    logger.error("fail to register lock, {}", illegalArgumentException.getMessage());
                }
            });
        }
    }

    /**
     * 注册自定义锁类型
     *
     * @param lockType 锁类型标识符
     * @param creator  锁创建函数接口
     */
    public static void registerLockType(String lockType, LockCreator creator) {
        if (lockType == null || lockType.trim().isEmpty()) {
            throw new IllegalArgumentException("Lock type cannot be null or empty");
        }
        if (creator == null) {
            throw new IllegalArgumentException("Lock creator cannot be null");
        }
        lockCreators.put(formatLockName(lockType), creator);
    }

    public static void registerLockType(String lockType, Class<? extends AbstractLock> lockClass, boolean noConnection) {
        if (lockType == null || lockType.trim().isEmpty()) {
            throw new IllegalArgumentException("Lock type cannot be null or empty");
        }
        if (lockClass == null) {
            throw new IllegalArgumentException("Lock class cannot be null");
        }

        if (noConnection) {
            lockCreators.put(lockType, (lockName, tryGetLockWaitMaxMs, lockExpireTimeMs, autoLock, dbConnection) -> createInstance(lockClass, lockName, tryGetLockWaitMaxMs, lockExpireTimeMs, autoLock));
        } else {
            lockCreators.put(lockType, (lockName, tryGetLockWaitMaxMs, lockExpireTimeMs, autoLock, dbConnection) -> createInstance(lockClass, lockName, tryGetLockWaitMaxMs, lockExpireTimeMs, autoLock, dbConnection));
        }
    }

    /**
     * 创建锁实例
     *
     * @param lockType            锁类型标识符
     * @param lockName            锁名称
     * @param tryGetLockWaitMaxMs 等待超时时间(ms)
     * @param lockExpireTimeMs    锁过期时间(ms)
     * @param autoLock            是否自动加锁
     * @return 锁实例
     * @throws LockException 锁操作异常
     */
    public static Lock createLock(String lockType, String lockName, long tryGetLockWaitMaxMs, long lockExpireTimeMs, boolean autoLock, DBConnection dbConnection) throws LockException {
        // 在第一次创建锁时触发自动注册扫描
        scanAndRegisterAutoLocks();

        if (lockType == null || lockType.trim().isEmpty()) {
            throw new IllegalArgumentException("Lock type cannot be null or empty");
        }

        lockType = formatLockName(lockType);
        LockCreator creator = lockCreators.get(lockType);
        if (creator == null) {
            throw new IllegalArgumentException("Unsupported lock type: " + lockType + ". Supported types: " + String.join(", ", lockCreators.keySet()));
        }

        try {
            return creator.create(lockName, tryGetLockWaitMaxMs, lockExpireTimeMs, autoLock, dbConnection);
        } catch (LockException e) {
            throw e;
        } catch (Exception e) {
            throw new LockException("Failed to create lock instance of type: " + lockType, e);
        }
    }

    public static Lock createLock(String lockType, String lockName, long tryGetLockWaitMaxMs, long lockExpireTimeMs, boolean autoLock) {
        return createLock(lockType, lockName, tryGetLockWaitMaxMs, lockExpireTimeMs, autoLock, null);
    }

    /**
     * 锁创建函数接口
     */
    @FunctionalInterface
    public interface LockCreator {
        Lock create(String lockName, long tryGetLockWaitMaxMs, long lockExpireTimeMs, boolean autoLock, DBConnection dbConnection) throws LockException;
    }

    /**
     * 创建锁实例
     */
    private static AbstractLock createInstance(Class<? extends AbstractLock> lockClass, String lockName, long tryGetLockWaitMaxMs, long lockExpireTimeMs, boolean autoLock, DBConnection dbConnection) throws LockException {
        try {
            Constructor<? extends AbstractLock> constructor = ClassUtils.getConstructorIfAvailable(lockClass, String.class, long.class, long.class, boolean.class, DBConnection.class);
            if (constructor == null) {
                throw new IllegalArgumentException("Lock class " + lockClass.getName() + " must have a constructor with parameters: (String lockName, long tryGetLockWaitMaxMs, long lockExpireTimeMs, boolean autoLock, DBConnection dbConnection)");
            }
            return constructor.newInstance(lockName, tryGetLockWaitMaxMs, lockExpireTimeMs, autoLock, dbConnection);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new LockException("Failed to create lock instance", e);
        }
    }

    private static AbstractLock createInstance(Class<? extends AbstractLock> lockClass, String lockName, long tryGetLockWaitMaxMs, long lockExpireTimeMs, boolean autoLock) throws LockException {
        try {
            Constructor<? extends AbstractLock> constructor = ClassUtils.getConstructorIfAvailable(lockClass, String.class, long.class, long.class, boolean.class);
            if (constructor == null) {
                throw new IllegalArgumentException("Lock class " + lockClass.getName() + " must have a constructor with parameters: (String lockName, long tryGetLockWaitMaxMs, long lockExpireTimeMs, boolean autoLock)");
            }
            return constructor.newInstance(lockName, tryGetLockWaitMaxMs, lockExpireTimeMs, autoLock);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new LockException("Failed to create lock instance", e);
        }
    }
}
